package com.saturday.feign;

import cn.hutool.core.codec.Base64;
import com.saturday.common.constants.CommonConsts;
import com.saturday.common.exception.BusinessException;
import com.saturday.common.exception.UserContextLoseException;
import feign.FeignException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletResponse;

import static com.saturday.common.constants.CommonConsts.FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY;
import static com.saturday.common.constants.CommonConsts.FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY;

@Slf4j
@Aspect
@Order(value = 100)
@Component
public class FeignExceptionAspect {

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.GetMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.PostMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.PutMapping)" +
            " || @annotation(org.springframework.web.bind.annotation.DeleteMapping)")
    public void pointcut() {
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) {
        try {
            Object proceed = joinPoint.proceed();
            return proceed;
        } catch (BusinessException e) {
            log.error("feign调用异常：{}", e.getMessage());
            if (null != RequestContextHolder.getRequestAttributes() && null != ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()) {
                HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
                response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY, e.getCode());
                response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY, Base64.encode(e.getMessage(), "UTF-8"));
            }
            throw e;
        } catch (UserContextLoseException e) {
            log.error("用户信息缺失：{}", e);
            if (null != RequestContextHolder.getRequestAttributes() && null != ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()) {
                HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
                response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY, e.getCode());
                response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY, Base64.encode(e.getMessage(), "UTF-8"));
            }
            throw e;
        } catch (FeignException e) {
            // 存在未发起远程调用前抛出的FeignException异常
            if (e instanceof FeignException.ServiceUnavailable) {
                FeignException.ServiceUnavailable serviceUnavailable = (FeignException.ServiceUnavailable) e;
                log.error(serviceUnavailable.getMessage());
                throw BusinessException.createException("服务不可用");
            }

            throw e;
        } catch (Throwable throwable) {
            Throwable cause = throwable.getCause();
            while (null != cause && null != cause.getCause()) {
                cause = cause.getCause();
            }

            if (cause instanceof BusinessException) {
                if (null != RequestContextHolder.getRequestAttributes() && null != ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()) {
                    HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
                    response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY, ((BusinessException) cause).getCode());
                    response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY, Base64.encode((cause).getMessage(), CommonConsts.UTF8));
                }
                throw (BusinessException) cause;
            } else if (cause instanceof UserContextLoseException) {
                if (null != RequestContextHolder.getRequestAttributes() && null != ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse()) {
                    HttpServletResponse response = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
                    response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_CODE_KEY, ((UserContextLoseException) cause).getCode());
                    response.setHeader(FEIGN_RESPONSE_HEADER_ERROR_MESSAGE_KEY, Base64.encode((cause).getMessage(), CommonConsts.UTF8));
                }
                throw (UserContextLoseException) cause;
            }
            log.error("接口调用异常：{}", throwable);
            throw new RuntimeException("未知异常");
        }
    }

}
